import cn.hutool.core.util.StrUtil;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.listener.NamingEvent;
import com.alibaba.nacos.api.naming.pojo.Instance;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Collectors;

/**
 * description  nacos 服务权重选举
 *
 * @author TigerLin
 * @version 1.0
 * create 2024/06/25  00:11
 */
@Component
@ConditionalOnBean(WoodsElectWeightConfiguration.class)
@AutoConfigureAfter(NacosDiscoveryProperties.class)
@Slf4j
public class WoodsElectWeightListener implements ApplicationListener<ApplicationReadyEvent> {
    @Value("${spring.application.name}")
    String instanceName;
    @Value("${spring.cloud.nacos.discovery.server-addr}")
    String discoveryUrl;
    @Value("${spring.cloud.nacos.discovery.username:nacos}")
    String userName;
    @Value("${spring.cloud.nacos.discovery.password:nacos}")
    String password;

    @Resource
    NacosDiscoveryProperties nacosDiscoveryProperties;

    @Resource
    WoodsElectWeightConfiguration woodsElectWeightConfiguration;

    @Resource
    IWoodsElectFinishHandler iWoodsElectFinishHandler;

    NamingService namingService;

    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
//        Properties properties = new Properties();
//        properties.put(PropertyKeyConst.SERVER_ADDR, discoveryUrl);
//        properties.put(PropertyKeyConst.USERNAME, userName);
//        properties.put(PropertyKeyConst.PASSWORD, password);
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.SERVER_ADDR, nacosDiscoveryProperties.getServerAddr());
        properties.put(PropertyKeyConst.USERNAME, nacosDiscoveryProperties.getUsername());
        properties.put(PropertyKeyConst.PASSWORD, nacosDiscoveryProperties.getPassword());
        try{
            namingService = NacosFactory.createNamingService(properties);
            namingService.subscribe(
                    instanceName,
                    nacoseEvent -> {
                        if( nacoseEvent instanceof NamingEvent) {
                            List<Instance> instanceList = ((NamingEvent)(nacoseEvent)).getInstances();
                            List<Map<String,String>> metadataMapList = instanceList.stream()
                                    .map(Instance::getMetadata)
                                    .filter(map ->{
                                        String weightStr = map.get(WoodsElectWeightConfiguration.ELECT_WEIGHT_NAME);
                                        // 权重最小值为 主服务， 值小代表服务运行时间较久，可尽量减少服务抖动
                                        return StrUtil.isNotEmpty(weightStr) && Long.parseLong(weightStr) <= woodsElectWeightConfiguration.getElectWeight();
                                    })
                                    .collect(Collectors.toList());
                            if(metadataMapList.size() > 1){
                                log.info("选举失败，存在多个服务权重低于当前服务");
                                woodsElectWeightConfiguration.setElectStatus(false);
                                iWoodsElectFinishHandler.serviceStop();
                            }else{
                                log.info("选举成功，当前服务权重最高");
                                woodsElectWeightConfiguration.setElectStatus(true);
                                iWoodsElectFinishHandler.serviceStart();
                            }

                        }
                    }
            );
        } catch (NacosException e) {
            log.error("未能注册选举监听",e);
        }
    }
}
